IMPORTANT
You should never use only one instance of a notification mechanism to convey both input and output information, because doing so can easily cause confusion. For example, after posting a request, an application will at some point start waiting for results. If it waits on the same mechanism where the request was posted, the request itself may appear to be the result. The application may then clear the request in the mistaken belief that it was a result and no actual work gets done.
Before notifying a task, your application should make sure that everything the task needs is in memory. That is, you should have created any necessary queues and allocated space for any data the task may require. For each task, your application establishes the parameters of the work that it wants the task to perform and then it must signal the task through either a queue or a semaphore to begin performing that work. The specific work that the task is to perform can be completely defined within a message, or possibly within a block of memory reserved for that task. You can also pass in a pointer to the function that the task should call to perform the work. Doing so allows one task to perform many different types of chores. Listing 3-4 shows a function that divides up a large amount of data among multiple tasks, placing requests on each task's request queue and waiting for the results.Listing 3-4 Assigning work to tasks
OSErr NotifyTasks( UInt32 realFirstThing, UInt32 realTotalThings ) {
UInt32 i;
OSErr theErr;
UInt32 thingsPerTask;
UInt32 message;
sWorkParams appData;
theErr = noErr;
thingsPerTask = realTotalThings / numProcessors;
/* Start each task working on a unique piece of the total data */
for( i = 0; i < numProcessors; i++ ) {
myTaskData[i].params.firstThing =
realFirstThing + thingsPerTask * i;
myTaskData[i].params.totalThings = thingsPerTask;
message = kMyRequestOne;
MPNotifyQueue( myTaskData[i].requestQueue, (void *)message,
NULL, NULL );
}
/* Now wait for the tasks to finish */
for( i = 0; i < numProcessors; i++ )
MPWaitOnQueue( myTaskData[i].resultQueue, (void **)&message,
NULL, NULL, kDurationForever );
return( theErr );
}
For each task, it calls MPNotifyQueue to place the pointer to the task's portion of the data on the task's request queue. It then calls MPWaitOnQueue to wait for confirmation that the task has completed.
If you want to use semaphores or event groups instead of message queues, you would call the following functions to set up, notify, and wait on them, in a manner similar to that shown in Listing 3-4 :Note
A message queue message is passed to the queue as three 32-bit parameters. Because the message in Listing 3-4 is only 32-bits long, the remaining two parameters are set to NULL .